package io.renren.modules.mvs.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.renren.common.exception.RRException;
import io.renren.common.utils.Constant;
import io.renren.common.utils.MvsUtils;
import io.renren.common.utils.PageUtils;
import io.renren.modules.mvs.dao.MvsTicketDao;
import io.renren.modules.mvs.dao.MvsVehicleDao;
import io.renren.modules.mvs.dao.MvsViolationDao;
import io.renren.modules.mvs.dao.MvsViolationTypeDao;
import io.renren.modules.mvs.dto.TicketDto;
import io.renren.modules.mvs.dto.ViolationDto;
import io.renren.modules.mvs.entity.TicketEntity;
import io.renren.modules.mvs.entity.VehicleEntity;
import io.renren.modules.mvs.entity.ViolationEntity;
import io.renren.modules.mvs.entity.ViolationTypeEntity;
import io.renren.modules.mvs.service.TicketService;
import io.renren.modules.mvs.service.ViolationService;
import io.renren.modules.sys.dao.SysDictDao;
import io.renren.modules.sys.dao.SysRoleDao;
import io.renren.modules.sys.dao.SysUserDao;
import io.renren.modules.sys.entity.SysRoleEntity;
import io.renren.modules.sys.entity.SysUserEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

import static io.renren.common.utils.Constant.UNPAID;
import static io.renren.common.utils.Constant.VEHICLE_OWNER;
import static io.renren.common.utils.MvsUtils.calculateOverDueFee;


@Service
@Slf4j
public class ViolationServiceImpl extends ServiceImpl<MvsViolationDao, ViolationEntity> implements ViolationService {
    @Autowired
    private MvsVehicleDao mvsVehicleDao;
    @Autowired
    private MvsViolationDao mvsViolationDao;
    @Autowired
    private MvsViolationTypeDao mvsViolationTypeDao;
    @Autowired
    private MvsTicketDao mvsTicketDao;
    @Autowired
    private SysRoleDao sysRoleDao;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        long currPage = 1;
        long limit = 10;

        if (params.get(Constant.PAGE) != null) {
            currPage = Long.parseLong((String) params.get(Constant.PAGE));
        }
        if (params.get(Constant.LIMIT) != null) {
            limit = Long.parseLong((String) params.get(Constant.LIMIT));
        }
        IPage<ViolationDto> page = this.baseMapper.selectPageByCondition(new Page<>(currPage, limit), params);
        page.getRecords().sort(Comparator.comparing(ViolationDto::getViolationTime));

        //查询违章记录下的罚单信息
        List<Long> violationIds = page.getRecords().stream().map(ViolationDto::getId).collect(Collectors.toList());
        List<TicketDto> tickets = mvsTicketDao.selectListByViolationIds(violationIds);
        page.getRecords().forEach(item -> {
            for (TicketDto ticket : tickets) {
                if (ticket.getViolationId().equals(item.getId())) {
                    item.setTicket(ticket);
                    break;
                }
            }
        });

        return new PageUtils(page);
    }

    @Override
    public Map<String, List<?>> getLinkedData(List<String> plateNumbers, List<String> violationTypeNames) {
        HashMap<String, List<?>> map = new HashMap<>();
        // 获取机动车列表
        List<VehicleEntity> vehicleList = mvsVehicleDao.selectList(new LambdaQueryWrapper<VehicleEntity>()
                .select(VehicleEntity::getId, VehicleEntity::getPlateNumber, VehicleEntity::getUserId)
                .in(VehicleEntity::getPlateNumber, plateNumbers));
        map.put("vehicleList", vehicleList);
        // 获取违章类型列表
        List<ViolationTypeEntity> violationTypeList = mvsViolationTypeDao.selectList(new LambdaQueryWrapper<ViolationTypeEntity>()
                .select(ViolationTypeEntity::getId, ViolationTypeEntity::getViolationTypeName)
                .in(ViolationTypeEntity::getViolationTypeName, violationTypeNames));
        map.put("violationTypeList", violationTypeList);
        return map;
    }

    @Override
    public List<Long> getIdListByUserId(Long userId) {
        return this.baseMapper.selectIdListByUserId(userId);
    }

    @Override
    public Map<String, Collection<?>> getNumberListByDateRange(String startDate, String endDate) {
        if (StringUtils.isEmpty(startDate) || StringUtils.isEmpty(endDate)) {
            // 如果startDate或endDate为空，去过去最近一周作为时间范围
            LocalDate end = LocalDate.now();
            LocalDate start = end.minusDays(6);
            startDate = start.toString();
            endDate = end.toString();
        }
        LocalDate start = LocalDate.parse(startDate);
        LocalDate end = LocalDate.parse(endDate);
        MvsUtils.checkDate(start, end);

        SysUserEntity user = (SysUserEntity) SecurityUtils.getSubject().getPrincipal();
        List<SysRoleEntity> roles = sysRoleDao.queryListByUserId(user.getUserId());
        // 如果角色是机动车车主，则只能看到自己的罚单数据
        if (roles.size() == 1 && VEHICLE_OWNER.equals(roles.get(0).getRoleName())) {
            List<Map<String, ?>> dbDataList = this.baseMapper.selectCountByDateRange(startDate, endDate, user.getUserId());
            return MvsUtils.getStatisticByDateRange(start, end, dbDataList);
        }
        List<Map<String, ?>> dbDataList = this.baseMapper.selectCountByDateRange(startDate, endDate, null);
        return MvsUtils.getStatisticByDateRange(start, end, dbDataList);
    }

    private static final int INTERVAL = 10; //单位秒

    @Override
    public synchronized void saveViolation(ViolationEntity violation) {
        //0.为防止重复插入相同的违章记录和罚单，相同数据的违章记录在INTERVAL内只能插入一条
        //具体方法：从数据库查询数据相同且创建时间在 当前时间~当前时间-INTERVAL 范围的记录，如果存在则不能插入；并且要使用锁，防止高并发环境下重复插入
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime preTime = now.minusSeconds(INTERVAL);
        Integer count = lambdaQuery()
                .eq(ViolationEntity::getUserId, violation.getUserId())
                .eq(ViolationEntity::getVehicleId, violation.getVehicleId())
                .eq(ViolationEntity::getViolationTime, violation.getViolationTime())
                .eq(ViolationEntity::getViolationAddress, violation.getViolationAddress())
                .le(ViolationEntity::getCreateTime, now)
                .ge(ViolationEntity::getCreateTime, preTime)
                .count();
        if (count > 0) {
            throw new RRException("插入失败，数据库中在 " + now + " ~ " + preTime + " 这" + INTERVAL + "秒的时间范围内存在相同的数据");
        }
        ViolationServiceImpl proxy = (ViolationServiceImpl) AopContext.currentProxy();
        // 为防止this调用导致事务失效，使用当前对象的代理对象
        proxy.saveViolationAndTicket(violation);
    }

    @Override
    public ViolationDto getDetailInfoById(Long id) {
        return this.baseMapper.selectDetailInfoById(id);
    }

    @Transactional(rollbackFor = Exception.class)
    public void saveViolationAndTicket(ViolationEntity violation) {
        //1.保存违章记录
        this.baseMapper.insert(violation);
        //2.创建罚单
        TicketEntity ticket = new TicketEntity();
        ticket.setViolationId(violation.getId());
        ticket.setUserId(violation.getUserId());
        ticket.setCreateTime(violation.getViolationTime());
        createTicket(ticket);
    }

    private void createTicket(TicketEntity ticket) {
        //1.根据违章记录id查询违章记录表
        ViolationEntity violation = mvsViolationDao.selectById(ticket.getViolationId());
        //2.根据违章类型id查询罚金等信息
        ViolationTypeEntity violationType = mvsViolationTypeDao.selectById(violation.getViolationTypeId());
        //3.根据违章信息获取罚单需要的其它数据
        //3.1 计算违章天数
        LocalDate localDate = violation.getViolationTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        long dayNumber = ChronoUnit.DAYS.between(localDate, LocalDate.now());
        ticket.setViolationDay((int) (dayNumber < 0 ? 0 : dayNumber));
        //3.2 罚金
        BigDecimal fineAmount = violationType.getFineAmount();
        ticket.setFineAmount(fineAmount);
        ticket.setWaitFine(fineAmount);
        //3.3 计算滞纳金
        BigDecimal overdueFee = calculateOverDueFee(violation.getViolationDeadline(), fineAmount);
        ticket.setOverdueFee(overdueFee);
        //3.4 代缴滞纳金和罚单状态
        ticket.setWaitOverdueFee(overdueFee);
        ticket.setTicketStatus(UNPAID);
        //3.5 生成罚单编号
        ticket.setId(generateTicketNumber());

        mvsTicketDao.insert(ticket);
    }

    /**
     * 生成罚单id
     */
    private String generateTicketNumber() {
        // 获取当前时间戳（10位）
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHH");
        String timestamp = sdf.format(new Date());

        // 生成5位随机数
        Random random = new Random();
        int randomNumber = 10000 + random.nextInt(90000); // 确保是5位数
        String randomStr = String.valueOf(randomNumber);

        // 计算校验码（简单示例：各位数字之和取模10）
        String prefix = timestamp + randomStr;
        int checksum = calculateChecksum(prefix);

        // 组合成16位编码
        return prefix + checksum;
    }

    private int calculateChecksum(String input) {
        int sum = 0;
        for (char c : input.toCharArray()) {
            sum += Character.getNumericValue(c);
        }
        return sum % 10; // 取模10得到校验码
    }

}